﻿// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
//     <version>$Revision$</version>
// </file>

using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

using ICSharpCode.TextEditor.Document;

namespace ICSharpCode.TextEditor
{
	/// <summary>
	/// This class views the line numbers and folding markers.
	/// </summary>
	public class GutterMargin : AbstractMargin, IDisposable
	{
        int leftPadding = 3;
        int rightPadding = 7;

        int drawStyle = 0; //Draw Style Default 0

		StringFormat numberStringFormat = (StringFormat)StringFormat.GenericTypographic.Clone();
		
		public static Cursor RightLeftCursor;
		
		static GutterMargin()
		{             
            Stream cursorStream = Assembly.GetCallingAssembly().GetManifestResourceStream("ICSharpCode.TextEditor.Resources.RightArrow.cur");
			if (cursorStream == null) throw new Exception("could not find cursor resource");
			RightLeftCursor = new Cursor(cursorStream);
			cursorStream.Close();
		}
		
		public void Dispose()
		{
			numberStringFormat.Dispose();
		}
		
		public override Cursor Cursor {
			get {
				return RightLeftCursor;
			}
		}
		
		public override Size Size
        {
            get
            {
                return new Size((int)(textArea.TextView.WideSpaceWidth
                                      * Math.Max(4, (int)Math.Log10(textArea.Document.TotalNumberOfLines) + 2)),
                                //* Math.Max(3, (int)Math.Log10(textArea.Document.TotalNumberOfLines) + 1)),
                                -1);
            }
        }
		
		public override bool IsVisible {
			get {
				return textArea.TextEditorProperties.ShowLineNumbers;
			}
		}

		public GutterMargin(TextArea textArea) : base(textArea)
		{
            numberStringFormat.Alignment = StringAlignment.Far;
            numberStringFormat.LineAlignment = StringAlignment.Far;
			numberStringFormat.FormatFlags   = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.FitBlackBox |
				StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
		}

        public override void Paint(Graphics g, Rectangle rect)
        {
            if (rect.Width <= 0 || rect.Height <= 0)
            {
                return;
            }

            HighlightColor foldLinePainterColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine");
            HighlightColor lineNumberPainterColor = textArea.Document.HighlightingStrategy.GetColorFor("LineNumbers");
            int fontHeight = textArea.TextView.FontHeight;
            Brush fillBrush = textArea.Enabled ? BrushRegistry.GetBrush(lineNumberPainterColor.BackgroundColor) : textArea.TextView.UnenableBackgroundBrush;
            Brush drawBrush = BrushRegistry.GetBrush(lineNumberPainterColor.Color);
            Brush markBrush = new SolidBrush(Color.FromArgb(127, 223, 127));

            if (!textArea.Enabled)
            {
                g.FillRectangle(fillBrush, rect);
            }

            for (int y = 0; y < (DrawingPosition.Height + textArea.TextView.VisibleLineDrawingRemainder) / fontHeight + 1; ++y)
            {
                int ypos = drawingPosition.Y + fontHeight * y - textArea.TextView.VisibleLineDrawingRemainder;
                Rectangle backgroundRectangle = new Rectangle(drawingPosition.X, ypos, drawingPosition.Width, fontHeight);
                Rectangle stringRectangle = new Rectangle(backgroundRectangle.X + leftPadding, backgroundRectangle.Y, backgroundRectangle.Width - leftPadding - rightPadding, backgroundRectangle.Height);
                if (rect.IntersectsWith(backgroundRectangle))
                {
                    int curLine = textArea.Document.GetFirstLogicalLine(textArea.Document.GetVisibleLine(textArea.TextView.FirstVisibleLine) + y);

                    if (curLine < textArea.Document.TotalNumberOfLines)
                    {
                        Font lineFont = lineNumberPainterColor.GetFont(TextEditorProperties.FontContainer);

                        if (textArea.Document.TextEditorProperties.ComparisonState)
                        {
                            DrawLineCustomBackground(g,
                                curLine,
                                lineFont,
                                markBrush,
                                drawBrush,
                                backgroundRectangle);
                        }
                        else
                        {
                            DrawLineMarkBackground(g,
                                curLine,
                                lineFont,
                                markBrush,
                                drawBrush,
                                backgroundRectangle);
                        }                        

                        g.DrawString((curLine + 1).ToString(),
                                     lineFont,
                                     drawBrush,
                                     stringRectangle,
                                     numberStringFormat);

                    }

                }                 

            }

            //行号绘制边线
            g.DrawLine(new Pen(foldLinePainterColor.Color),
                       base.drawingPosition.X + drawingPosition.Width - 1,
                       rect.Top,
                       base.drawingPosition.X + drawingPosition.Width - 1,
                       rect.Bottom);

        }

        public override void HandleMouseDown(Point mousepos, MouseButtons mouseButtons)
        {
            TextLocation selectionStartPos;

            if (!IsVisible)
                return;

            textArea.SelectionManager.selectFrom.where = WhereFrom.Gutter;
            int realline = textArea.TextView.GetLogicalLine(mousepos.Y);
            if (realline >= 0 && realline < textArea.Document.TotalNumberOfLines)
            {
                // shift-select
                if ((Control.ModifierKeys & Keys.Shift) != 0)
                {
                    if (!textArea.SelectionManager.HasSomethingSelected && realline != textArea.Caret.Position.Y)
                    {
                        if (realline >= textArea.Caret.Position.Y)
                        { // at or below starting selection, place the cursor on the next line
                          // nothing is selected so make a new selection from cursor
                            selectionStartPos = textArea.Caret.Position;
                            // whole line selection - start of line to start of next line
                            if (realline < textArea.Document.TotalNumberOfLines - 1)
                            {
                                textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(0, realline + 1)));
                                textArea.Caret.Position = new TextLocation(0, realline + 1);
                            }
                            else
                            {
                                textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, realline)));
                                textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, realline);
                            }
                        }
                        else
                        { // prior lines to starting selection, place the cursor on the same line as the new selection
                          // nothing is selected so make a new selection from cursor
                            selectionStartPos = textArea.Caret.Position;
                            // whole line selection - start of line to start of next line
                            textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(selectionStartPos.X, selectionStartPos.Y)));
                            textArea.SelectionManager.ExtendSelection(new TextLocation(selectionStartPos.X, selectionStartPos.Y), new TextLocation(0, realline));
                            textArea.Caret.Position = new TextLocation(0, realline);
                        }
                    }
                    else
                    {
                        // let MouseMove handle a shift-click in a gutter
                        MouseEventArgs e = new MouseEventArgs(mouseButtons, 1, mousepos.X, mousepos.Y, 0);
                        textArea.RaiseMouseMove(e);
                    }
                }
                else
                { // this is a new selection with no shift-key
                  // sync the textareamousehandler mouse location
                  // (fixes problem with clicking out into a menu then back to the gutter whilst
                  // there is a selection)
                    textArea.mousepos = mousepos;

                    selectionStartPos = new TextLocation(0, realline);
                    textArea.SelectionManager.ClearSelection();
                    // whole line selection - start of line to start of next line
                    if (realline < textArea.Document.TotalNumberOfLines - 1)
                    {
                        textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(selectionStartPos.X, selectionStartPos.Y + 1)));
                        //textArea.Caret.Position = new TextLocation(selectionStartPos.X, selectionStartPos.Y + 1);
                        textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y);
                    }
                    else
                    {
                        textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, new TextLocation(0, realline), new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y)));
                        textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y);
                    }
                }
            }
        }

        protected virtual void DrawLineMarkBackground(Graphics g,int curLine, Font lineFont, Brush markBrush, Brush drawBrush, Rectangle backgroundRectangle)
        {
            if (!string.IsNullOrEmpty(Document.GetLineSegment(curLine).LineMark))
            {
                Rectangle lineMarkRect = new Rectangle(backgroundRectangle.X + backgroundRectangle.Width - leftPadding - 2, backgroundRectangle.Y, 3, backgroundRectangle.Height);
                g.FillRectangle(markBrush, lineMarkRect);
            }
        }

        protected virtual void DrawLineCustomBackground(Graphics g, int curLine, Font lineFont, Brush markBrush, Brush drawBrush, Rectangle backgroundRectangle)
        {
            if (textArea.Document.LineCustomBackgroundColors.ContainsKey(curLine))
            {
                CustomLineColor customLineColor = textArea.Document.LineCustomBackgroundColors[curLine];
                string customlineTag = customLineColor == null ? "" : customLineColor.Tag;

                if (!string.IsNullOrEmpty(customlineTag))
                {
                    Brush greenBrush = Brushes.Red;
                    Brush redBrush = Brushes.Red;
                    Brush yellowBrush = Brushes.Red;                    

                    Rectangle lineMarkRect = new Rectangle(backgroundRectangle.X + backgroundRectangle.Width - leftPadding - 2, backgroundRectangle.Y, 3, backgroundRectangle.Height);

                    switch (customlineTag)
                    {
                        case "add":
                        case "Inserted":
                            markBrush = greenBrush;
                            drawBrush = Brushes.Gray;
                            break;
                        case "modify":
                        case "Modified":
                            markBrush = redBrush;
                            drawBrush = Brushes.Gray;
                            break;
                        case "delete":
                        case "Deleted":
                            markBrush = yellowBrush;
                            drawBrush = Brushes.Gray;
                            break;
                    }

                    g.FillRectangle(markBrush, lineMarkRect);

                }
            }
        }
    }
}
